import numpy as np
import math as m

class TODIM:

    matrix = None               #初始决策矩阵
    weight = None               #初始权重
    q = None                    #广义正交次方
    sumMatrix = []              #每个属性求和后的矩阵
    normMatrixD = None          #规范化后的矩阵
    ScoreMatrix = []            #计算后的得分函数
    delta = None                #偏好指数矩阵
    wref = None                 #最大权重
    theta = None                #损耗的衰减系数
    row = None                  #行
    col = None                  #列
    dominanceDegree = None      #每个方案的优势度

    def __init__(self, DecisionMatrix, weight, q, theta=1):
        '''
        :param DecisionMatrix: 决策矩阵
        :param wight: 属性权重
        :param q: q值
        '''
        self.matrix = DecisionMatrix
        self.weight = weight
        self.q = q
        self.normMatrixD = np.zeros(self.matrix.shape,dtype=float)
        self.theta = theta
        self.row = DecisionMatrix.shape[0]
        self.col = DecisionMatrix.shape[1]
        self.dominanceDegree = np.zeros([self.row, 1], dtype=float)
        self.delta = np.zeros([self.row, self.row])

    def addition(self,Ivq1,Ivq2,q):
        '''
        加法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a,b,c,d=Ivq1[0][0],Ivq1[0][1],Ivq1[1][0],Ivq1[1][1]
        e,f,g,h=Ivq2[0][0],Ivq2[0][1],Ivq2[1][0],Ivq2[1][1]
        u1=a**q+e**q-(a**q)*(e**q)
        u2=b**q+f**q-(b**q)*(f**q)
        u1 = u1**(1/3)
        u2 = u2**(1/3)
        v1=c*g
        v2=h*d
        return ([u1,u2],[v1,v2])


    def sum(self):
        '''
        对每列属性进行求和，为了后续的规范化处理
        1/x利用x/x**2计算所得
        '''
        costindex = (0, 1)
        column = len(self.matrix[0])
        for i in range(column):
            if i in costindex:
                point1 = self.matrix[0][i]
                point_2 = self.Exponentiation(point1,self.q, 2)
                point = self.division(point1,point_2,self.q)
                for j in range(1,len(self.matrix)):
                    points = self.matrix[j][i]
                    points_2 = self.Exponentiation(points, self.q, 2)
                    points = self.division(points, points_2,self.q)
                    point = self.addition(point,points, self.q)
            else:
                point = self.matrix[0][i]
                for j in range(1, len(self.matrix)):
                    point = self.addition(point, self.matrix[j][i], self.q)
            self.sumMatrix.append(point)


    def subtraction(self,Ivq1,Ivq2,q):
        '''
        减法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a,b,c,d=Ivq1[0][0],Ivq1[0][1],Ivq1[1][0],Ivq1[1][1]
        e,f,g,h=Ivq2[0][0],Ivq2[0][1],Ivq2[1][0],Ivq2[1][1]
        u1=a*g
        u2=b*h
        v1=(c**q+e**q-(c**q)*(e**q))**(1/q)
        v2=(d**q+f**q-(d**q)*(f**q))**(1/q)
        return ([u1, u2],[v1, v2])

    def Exponentiation(self,Ivq1,q,n):
        '''
        次方运算
        :param Ivq1:
        :param q:
        :param n:开多少次方
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        u1 = a ** n
        u2 = b ** n
        v1 =(1-((1-(c**q))**n))**(1/q)
        v2 =(1-((1-(d**q))**n))**(1/q)
        return ([u1, u2], [v1, v2])

    def multiplication(self,Ivq1,Ivq2,q):
        '''
        乘法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = a * e
        u2 = b * f
        v1 = (c ** q + g ** q - (c ** q) * (g ** q)) ** (1 / q)
        v2 = (d ** q + h ** q - (d ** q) * (h ** q)) ** (1 / q)
        return ([u1, u2], [v1, v2])

    def division(self,Ivq1,Ivq2,q):
        '''
        除法
        :param Ivq1:区间值
        :param Ivq2:区间值
        :param q:q值
        :return:
        '''
        a, b, c, d = Ivq1[0][0], Ivq1[0][1], Ivq1[1][0], Ivq1[1][1]
        e, f, g, h = Ivq2[0][0], Ivq2[0][1], Ivq2[1][0], Ivq2[1][1]
        u1 = (a ** q + g ** q - (a ** q) * (g ** q)) ** (1 / q)
        u2 = (b ** q + h ** q - (b ** q) * (h ** q)) ** (1 / q)
        v1 = c*e
        v2 = d*f
        return ([u1, u2], [v1, v2])


    # def NormalizedMatrix(self):
    #     '''
    #     规范化矩阵
    #     :param Matrix:决策矩阵
    #     :param q:q值
    #     :return:Matrix规范化之后的矩阵
    #     '''
    #     self.sum()
    #     for i in range(len(Matrix)):
    #         for j in range(len(Matrix[0])):
    #             self.normMatrixD[i][j] = self.division(self.matrix[i][j],self.sumMatrix[j],self.q)

    def NormalizedMatrix(self):
        '''
        规范化矩阵
        :param Matrix:决策矩阵
        :param q:q值
        :return:Matrix规范化之后的矩阵
        '''
        self.sum()
        costindex = (0, 1)
        for i in range(len(Matrix)):
            for j in range(len(Matrix[0])):
                if j in costindex:
                    point = self.normMatrixD[i][j]
                    point_2 = self.Exponentiation(point,self.q,2)
                    point = self.division(point,point_2,self.q)
                    self.normMatrixD[i][j] = self.division(point, self.sumMatrix[j], self.q)
                else:
                    self.normMatrixD[i][j] = self.division(self.matrix[i][j],self.sumMatrix[j],self.q)


    def normalizeWeights(self):
        maxWeight = np.max(self.weight)
        self.weight = self.weight / maxWeight


    def getscore(self,IVq):
        '''
        我们提出的得分函数
        :param IVq:区间值
        :param q:q值
        :return:
        '''
        a = IVq[0][0]
        b = IVq[0][1]
        c = IVq[1][0]
        d = IVq[1][1]
        return (1/2) * (a**self.q + b**self.q - c**self.q - d**self.q)
        #return (m.log(a + b + c + d + 1) + (b - a) + (d - c) ** 2 + (a - c + b - d) * m.log(3) / 2) / (2 * m.log(3))



    def getMarginalUtilityScore(self):
        '''
        获取边际效用得分
        :param NormalizedMatrix:规范化矩阵
        :return:
        '''
        self.NormalizedMatrix()

        for Row in range(len(self.normMatrixD)):
            Scoreline=[]
            for Column in range(len(self.normMatrixD[0])):
                t=self.getscore(self.normMatrixD[Row][Column])
                Scoreline.append(t)
            self.ScoreMatrix.append(Scoreline)





    def getGrauDominio(self, verbose=False):
        '''
        计算每个方案的优势度
        即overall dominance degree
        '''
        self.getDelta()
        # self.printDelta()
        aux = self.delta.sum(axis=1)
        rank = [i + 1 for i in range(self.row)]
        for i in range(self.row):
            self.dominanceDegree[i] = (aux[i] - aux.min()) / (aux.max() - aux.min())
        self.dominanceDegree = self.dominanceDegree.flatten()
        # print(f'U={np.round(self.dominanceDegree, 4)}')

        b = sorted(self.dominanceDegree,reverse=True)
        for i in range(self.row):
            rank[i] = b.index(self.dominanceDegree[i])+1
        self.dominanceDegree=[i/sum(self.dominanceDegree) for i in self.dominanceDegree]

        return self.dominanceDegree


    def getDelta(self):
        '''
        计算方案优势度矩阵
        '''
        self.normalizeWeights()
        self.getMarginalUtilityScore()
        for i in range(self.row):
            for j in range(self.row):
                self.delta[i, j] = self.getSumPhi(i, j)
        # self.printDelta()

    def getSumPhi(self, i, j):
        '''
        每个方案所有属性优势度之和
        '''
        # m = np.zeros(self.nCri)
        m = 0
        for c in range(self.col):
            m = m + self.getPhi(i, j, c)
            # self.printPhi()
        return m

    def getPhi(self, i, j, c):
        '''
        '''
        sumWRef = self.getSumWRef()
        dij = self.getDistance(i, j, c)
        comp = self.getComparison(i, j, c)
        if comp == 0:
            return 0
        elif comp > 0:
            return np.sqrt((self.weight[c]*abs(dij))/sumWRef)
        else:
            return np.sqrt((sumWRef*abs(dij))/self.weight[c])/(-self.theta)


    def getSumWRef(self):
        '''
        计算总权重和
        :return:
        '''
        sumWRef = 0
        for c in self.weight:
            sumWRef += c
        return sumWRef

    def getDistance(self, alt_i, alt_j, crit):
        return (self.ScoreMatrix[alt_i][crit] - self.ScoreMatrix[alt_j][crit])

    # funcao modular para possibilitar outros tipos de comparações
    def getComparison(self, alt_i, alt_j, crit):
        return self.getDistance(alt_i, alt_j, crit)



if __name__ == "__main__":
    Matrix = np.array([[([0.58, 0.75], [0.1, 0.2]), ([0.7, 0.78], [0.2, 0.3]), ([0.5, 0.78], [0.1, 0.2]),
                        ([0.65, 0.7], [0.3, 0.35]), ([0.4, 0.5], [0.5, 0.6])],
                       [([0.5, 0.6], [0.1, 0.2]), ([0.75, 0.78], [0.2, 0.3]), ([0.6, 0.78], [0.1, 0.15]),
                       ([0.7, 0.8], [0.2, 0.3]), ([0.5, 0.6], [0.4, 0.5])],
                       [([0.5, 0.78], [0.1, 0.2]), ([0.6, 0.7], [0.3, 0.4]), ([0.7, 0.78], [0.2, 0.3]), ([0.6, 0.7], [0.3, 0.4]),
                       ([0.35, 0.45], [0.5, 0.65])],
                       [([0.75, 0.780], [0.2, 0.3]), ([0.6, 0.75], [0.2, 0.3]), ([0.7, 0.78], [0.2, 0.3]),
                       ([0.55, 0.65], [0.3, 0.4]), ([0.35, 0.4], [0.6, 0.65])],
                       [([0.6, 0.75], [0.3, 0.4]), ([0.5, 0.6], [0.4, 0.5]), ([0.6, 0.7], [0.3, 0.4]), ([0.5, 0.6], [0.4, 0.5]),
                       ([0.1, 0.2], [0.4, 0.5])]])

    weight = (0.2, 0.2, 0.2, 0.2, 0.2)

    todim = TODIM(Matrix, weight, 3)

    t=todim.getGrauDominio()
    print(t)